<?php
if(!defined('SYSTEM_PATH')) die('Cannot access this page directly or see your configuration file ') ;
/**
 * Loader
 * 
 * @desc Loader de classe
 * @package CosmoClan
 * @author menTiss - Staff SeS
 * @copyright 2007-2008
 * @version 0.2.0
 * @access public
 **/
final class Loader extends Error {
	 private static $_instance = null ;
	 private static $_included = array() ;
	 private static $_helpers  = array() ;
	 private $_listClass 	  = array() ;
	 private $_listDatabase 	  = array() ;
	 private $_listControllers = array() ;
	 private $_listModels	  = array() ;
	 private $_listBlocks	  = array() ;
	 private $_lang 		  = array() ;
	 private $db ;
	
	/**
	 * Loader::getInstance()
	 * 
	 * @desc Allow to create or return instance of the Loader class
	 * 
	 * @return object
	 */
	public static function &getInstance() {		
		if (is_null(self::$_instance))
			self::$_instance = new Loader();
    		return self::$_instance;
	}
	/**
	 * 
	 * @desc Regarde si le fichier de classe existe et le charge. Permet d'instancier la classe si le 2eme paramètre vaut true.
	 * 
	 * @param string $class
	 * @param bool $instantiate
	 * 
	 * @return mixed (bool or object)
	 **/
	 public function &object($class) {
		if($this->isLoaded($class,'class')) return $this->_listClass[strtolower($class)] ;
		
		if(ereg('.',$class)) {
			$extends = explode('.',strtolower($class)) ;
			Loader::includeClass($extends[1]) ;
			$name = ucfirst($extends[0]).ucfirst($extends[1]) ;
		} else {
			$name = ucfirst(strtolower($class)) ;
		}
		
		if(file_exists(SITE_PATH.'classes/'.strtolower($class).EXT)) {
			include_once(SITE_PATH.'classes/'.strtolower($class).EXT) ;				
				${strtolower($class)} = new $name() ;
				$this->_listClass[strtolower($class)] =& ${strtolower($class)} ;
				$this->addLog('debug', 'The class '.$class.' has been initialized') ;
				return ${strtolower($class)} ;
		} elseif(file_exists(SYSTEM_PATH.'classes/'.strtolower($class).EXT)) {
			include_once(SYSTEM_PATH.'classes/'.strtolower($class).EXT) ;	
				${strtolower($class)} = new $name() ;
				$this->_listClass[strtolower($class)] =& ${strtolower($class)} ;
				$this->addLog('debug', 'The class '.$class.' has been initialized') ;
				return ${strtolower($class)} ;
		} else {
			$this->addLog('error', 'The class requested ('.$class.') does not exists') ;
			$this->setError('Loader cannot find the '.$class.' you requested') ;			
		}
		return false ;
	}	
	/**
	 * Loader::setDatabase()
	 * 
	 * @param object $db
	 * 
	 * @return void
	 */
	public function setDatabase(&$db) {
		$this->db =& $db ;
	}
	/**
	 * Loader::helper()
	 * 
	 * @param string $file
	 * 
	 * @return bool
	 */
	public static function helper($file) {
		if(in_array(strtolower($file), self::$_helpers))
			return true;
	
		if(file_exists(SITE_PATH.'helpers/'.strtolower($file).EXT)) {
			include_once(SITE_PATH.'helpers/'.strtolower($file).EXT) ;
			self::$_helpers[] = strtolower($file) ;
			return true;
		} elseif(file_exists(SYSTEM_PATH.'helpers/'.strtolower($file).EXT)) {
			include_once(SYSTEM_PATH.'helpers/'.strtolower($file).EXT) ;
			self::$_helpers[] = strtolower($file) ;
			return true;
		}
		
		return false;
	}
	/**
	 * Loader::includeClass()
	 * 
	 * @desc 	Include a file (generally a class file) from serveral directory
	 * 		The second parameter is used to switch on include methods : 
	 * 			- auto is used to let function choose the directory to include the file (first check in application classes directory then in core system classes directory) => default behavior
	 * 			- site/application is used to include the file from the application classes directory
	 * 			- system/core is used to include from the core system classes directory
	 * 			- persosys is used to include from the core system base dire
	 * 
	 * @param mixed $file
	 * @param string $where
	 * 
	 * @return bool
	 */
	public static function includeClass($file, $where = 'auto') {
		if(in_array(strtolower($file), self::$_included)) {
			return true;
		}
			
		switch(strtolower($where)) {
			case 'auto' :
				if(file_exists(SITE_PATH.'classes/'.$file.EXT)) {
					include_once(SITE_PATH.'classes/'.$file.EXT) ;
					self::$_included[] = $file ;
					return true;
				} elseif(file_exists(SYSTEM_PATH.'classes/'.$file.EXT)) {
					include_once(SYSTEM_PATH.'classes/'.$file.EXT) ;	
					self::$_included[] = $file ;
					return true;
				}
				break ;
			case 'site_class' 		:
			case 'application_class' :
				if(file_exists(SITE_PATH.'classes/'.$file.EXT)) {
					include_once(SITE_PATH.'classes/'.$file.EXT) ;
					self::$_included[] = $file ;
					return true;
				}
				break ;
			case 'system_class' :
			case 'core_class' 	:
				if(file_exists(SYSTEM_PATH.'classes/'.$file.EXT)) {
					include_once(SYSTEM_PATH.'classes/'.$file.EXT) ;	
					self::$_included[] = $file ;
					return true;
				}
				break ;
			case 'system' 	:
			case 'core'	:
				if(file_exists(SYSTEM_PATH.$file.EXT)) {
					include_once(SYSTEM_PATH.$file.EXT) ;
					self::$_included[] = $file ;
					return true;
				}
				break ;
			case 'site' 	:
			case 'application'	:
				if(file_exists(SITE_PATH.$file.EXT)) {
					include_once(SITE_PATH.$file.EXT) ;
					self::$_included[] = $file ;
					return true;
				}
				break ;
			default : // do nothing
		}
		return false;
	}
	/**
	 * Loader::ajaxController()
	 * 
	 * @desc Return an instance of AjaxController that you specified if exists.
	 * 
	 * @param string $controller
	 * 
	 * @return mixed
	 */
	public function &ajaxController($controller) {
		if($this->isLoaded($controller,'controller')) return $this->_listControllers[strtolower($controller).'_ajax'] ;
		
		if(file_exists(SITE_PATH.'controllers/ajax/'.strtolower($controller).EXT)) {
			include_once(SITE_PATH.'controllers/ajax/'.strtolower($controller).EXT) ;
			$name = ucfirst(strtolower($controller)).'AjaxController' ;
			$this->_listControllers[strtolower($controller).'_ajax'] = new $name() ;
			$this->addLog('debug', 'The controller '.$controller.' has been initialized') ;
			return $this->_listControllers[strtolower($controller).'_ajax'] ;
		} 
		$this->addLog('error','Controller '.SITE_PATH.'controllers/'.strtolower($controller).EXT.' does not exist or is missing') ;
		$this->setError('Loader cannot find the controller '.$controller.' you requested') ;			
		return $return = false ;
	}
	/**
	 * Loader::controller()
	 * 
	 * @desc Return an instance of Controller that you specified if exists.
	 * 
	 * @param string $module
	 * @param boolean $instantiate
	 * 
	 * @return mixed
	 */
	public function &controller($controller, $instantiate = true) {
		if($this->isLoaded($controller,'controller')) return $this->_listControllers[strtolower($controller)] ;
		
		if(file_exists(SITE_PATH.'controllers/'.strtolower($controller).'.global'.EXT)) {
			Loader::includeClass('global.controller') ;
			include_once(SITE_PATH.'controllers/'.strtolower($controller).'.global'.EXT) ;
			if($instantiate === true) {
				$name = ucfirst(strtolower($controller)).'GlobalController' ;
				$this->_listControllers[strtolower($controller)] = new $name() ;
				$this->addLog('debug', 'The global controller '.$controller.' has been initialized') ;
				return $this->_listControllers[strtolower($controller)] ;
			}
			return true ;
		} elseif(file_exists(SITE_PATH.'controllers/'.strtolower($controller).EXT)) {
			include_once(SITE_PATH.'controllers/'.strtolower($controller).EXT) ;
			if($instantiate === true) {
				$name = ucfirst(strtolower($controller)).'Controller' ;
				$this->_listControllers[strtolower($controller)] = new $name() ;
				$this->addLog('debug', 'The controller '.$controller.' has been initialized') ;
				return $this->_listControllers[strtolower($controller)] ;
			}
			return true ;
		} 
		$this->addLog('error','Controller '.SITE_PATH.'controllers/'.strtolower($controller).EXT.' does not exist or is missing') ;
		$this->setError('Loader cannot find the controller '.$controller.' you requested') ;			
		return false ;
	}
	/**
	 * Loader::model()
	 * 
	 * @param string $module
	 * @param boolean $instantiate
	 * 
	 * @return mixed
	 */
	public function &model($model, $instantiate = true) {
		if($this->isLoaded($model,'model')) return $this->_listModels[strtolower($model)] ;
		
		if(file_exists(SITE_PATH.'models/'.strtolower($model).EXT)) {
			include_once(SITE_PATH.'models/'.strtolower($model).EXT) ;
			if($instantiate === true) {
				$name = ucfirst(strtolower($model)).'Model' ;
				$this->_listModels[strtolower($model)] = new $name() ;
				$this->addLog('debug', 'The model '.$model.' has been initialized') ;
				return $this->_listModels[strtolower($model)] ;
			}
			return true ;
		} 
		$this->addLog('error','Model '.SITE_PATH.'models/'.strtolower($model).EXT.' does not exist or is missing') ;
		$this->setError('Loader cannot find the model '.$model.' you requested') ;			
		return false ;
	}
	/**
	 * Loader::block()
	 * 
	 * @param string $block
	 * @param string $action
	 * 
	 * @return mixed 
	 */
	public function block($block, $action = null) {
		if($this->isLoaded($block,'block')) {
			if(!is_null($action) && method_exists($this->_listBlocks[strtolower($block)], $action)) {
				// execute method on the object if exists
				return $this->_listBlocks[strtolower($block)]->$action() ;
			}
			return $this->_listBlocks[strtolower($block)];
		}
		
		
		if(file_exists(SITE_PATH.'blocks/'.strtolower($block).EXT)) {
			include_once(SITE_PATH.'blocks/'.strtolower($block).EXT) ;
			$name = ucfirst(strtolower($block)).'Block' ;
			$this->_listBlocks[strtolower($block)] = new $name() ;
			$this->addLog('debug', 'The block '.$block.' has been initialized') ;
			if(!is_null($action) && method_exists($this->_listBlocks[strtolower($block)], $action)) {
				// execute method on the object if exists
				return $this->_listBlocks[strtolower($block)]->$action() ;
			}
			return $this->_listBlocks[strtolower($block)];
		}
		$this->addLog('warning', 'Block '.SITE_PATH.'blocks/'.strtolower($block).EXT.' is missing') ;
		$this->setError('Loader cannot find the block '.$block.' you requested') ;
		return false;
	}
	/**
	 * $this->isLoaded()
	 * 
	 * @desc La classe a-t-elle déjà été déclaré ?
	 * 
	 * @param string $class
	 * @param string $type
	 * 
	 * @return boolean
	 **/
	private function isLoaded($class,$type) {
		switch($type) {
			case 'module'		: 	return isset($this->_listModules[strtolower($class)]) ;
			case 'class' 		:	return isset($this->_listClass[strtolower($class)]) ;
			case 'database' 	:	return isset($this->_listDatabase[strtolower($class)]) ;
			case 'block'		: 	return isset($this->_listBlocks[strtolower($class)]);
			default 			: 	return false ;
		}
	}	
	/**
	 * Loader::database()
	 * 
	 * @param string $type
	 * @param boolean $return
	 * 
	 * @return mixed (bool or object)
	 */
	public function database($type = 'default',$return = true) {
		if($this->isLoaded($type,'database')) {
			return $this->_listDatabase[strtolower($type)] ;
		}
		$config = $this->config('database') ;
		if(isset($config[$type])) {
			if(file_exists(SYSTEM_PATH.'database/driver/'.$config[$type]['driver'].'/query'.EXT)) {				
				include_once(SYSTEM_PATH.'database/driver/'.$config[$type]['driver'].'/query'.EXT) ;
				$className = $config[$type]['driver'].'Query' ;
				$driver = new $className($config[$type]) ;
				$this->_listDatabase[strtolower($type)] =& $driver ;
				$this->db->setDriver($driver) ;
				$this->db->connect() ;
				$this->addLog('debug', 'Database driver initialized : '.$config[$type]['driver']) ;
				if($return === true) return $this->db ;
				else return true ;
			} 
		}
		return false ;
	}
	/**
	 * Loader::databaseResult()
	 * 
	 * @param resource $driver
	 * 
	 * @return object
	 */
	public function databaseResult($driver) {
		if(file_exists(SYSTEM_PATH.'database/driver/'.$driver.'/result'.EXT)) {
			include_once(SYSTEM_PATH.'database/driver/'.$driver.'/result'.EXT) ;
			$className = $driver.'Result' ;
			return new $className() ;
			
		}
	}
	/**
	 * $this->config()
	 * 
	 * @param string $file
	 * 
	 * @return array
	 **/
	public function config($file) {
		$this->addLog('debug','Including config file : '.$file) ;
		include(CONF_PATH.$file.EXT) ;
		return $config ;
	}
	/**
	 * $this->language()
	 * 
	 * @param string $file
	 * 
	 * @return array
	 **/
	public function language($file) {
		if(file_exists(SITE_PATH.'lang/'.$file.EXT)) {
			$this->addLog('debug','Including site language file '. $file) ;
			include_once(SITE_PATH.'lang/'.$file.EXT) ;
			$this->_lang = array_merge($this->_lang,(array) $lang) ;
		} elseif(file_exists(SYSTEM_PATH.'lang/'.$file.EXT)) {
			$this->addLog('debug','Including system language file '. $file) ;
			include_once(SYSTEM_PATH.'lang/'.$file.EXT) ;
			$this->_lang = array_merge($this->_lang,(array) $lang) ;
		} else $this->addLog('debug', 'Language file '.$file.' is missing') ;
		return $this->_lang ;
	}
	public function classStringToPath($context) {
	}
}

?>